#include "yasaiworker.h"

YasaiWorker::YasaiWorker(int scrollbarWidth, QObject *parent)
    :
    QObject(parent),
    mScrollbarWidth(scrollbarWidth)
{
}

YasaiWorker::~YasaiWorker()
{    
}

void YasaiWorker::on_started()
{
    mArchiveFileHandler = new Lib7zipArchiveHandler(this);
}

void YasaiWorker::on_finished()
{
    delete mArchiveFileHandler;
}

void YasaiWorker::openFile(const QFileInfo &fileInfo, const QString &place, const QRect &viewRect, Utils::ViewOptions viewOptions, int adjustment)
{
    if (Utils::isImage(fileInfo.suffix()))
    {
        if (!mArchiveFileHandler->workingFilePath().isEmpty())
        {
            mArchiveFileHandler->closeWorkingArchive();
        }
        // ignore side-by-side if set since viewing single images
        // TODO allow viewing of images in directory side-by-side
        viewOptions &= ~Utils::ViewSideBySide;
        QPixmap pm = this->fitPixmap(QPixmap(fileInfo.filePath()), viewRect, viewOptions, adjustment);
        emit showImage(pm);
        return;
    }

    emit this->workerBusy(tr("Opening %1").arg(fileInfo.filePath()));

    // QFuture<QStandardItemModel *> futureArchiveModel = QtConcurrent::run(mArchiveFileHandler, &Lib7zipArchiveHandler::openArchiveFile, fileInfo.filePath());
    QStandardItemModel *archiveModel = mArchiveFileHandler->openArchiveFile(fileInfo.filePath());

    archiveModel->sort(0);
    emit this->archiveLoaded(archiveModel, place);
    emit this->workerDone();
}

void YasaiWorker::loadArchiveImage(int pos, int nextPos, const QRect &viewRect, Utils::ViewOptions viewOptions, int adjustment)
{
    QByteArray *indexQba = mArchiveFileHandler->dataAt(pos);
    if (!viewOptions.testFlag(Utils::ViewSideBySide) || nextPos == -1)
    {
        emit this->showImage(this->loadAndFitPixmap(indexQba, viewRect, viewOptions&~Utils::ViewSideBySide, adjustment));
        return;
    }

    QFuture<QPixmap> futureIpm       = QtConcurrent::run(this, &YasaiWorker::loadAndFitPixmap, indexQba, viewRect, viewOptions, adjustment);

    QByteArray *nextQba              = mArchiveFileHandler->dataAt(nextPos);
    QFuture<QPixmap> futureNpm       = QtConcurrent::run(this, &YasaiWorker::loadAndFitPixmap, nextQba, viewRect, viewOptions, adjustment);
    QFuture<QPixmap> futureIpmSingle = QtConcurrent::run(this, &YasaiWorker::loadAndFitPixmap, indexQba, viewRect, viewOptions&~Utils::ViewSideBySide, adjustment);
    if (!viewOptions.testFlag(Utils::ViewDetectWidePages))
    {
        emit this->showTwoImages(futureIpm.result(), futureNpm.result());
        return;
    }
    // detect wide pages and add them to a list, used for pagination
    QPixmap indexPm = futureIpm.result();
    QPixmap nextPm  = futureNpm.result();
    bool indexIsWide = indexPm.width()>indexPm.height();
    bool nextIsWide  = nextPm.width()>nextPm.height();
    int widePos     = -1;
    int nextWidePos = -1;
    if (indexIsWide) widePos     = pos;
    if (nextIsWide)  nextWidePos = nextPos;
    emit detectedWidePages(widePos, nextWidePos);

    if (!indexIsWide && !nextIsWide)
    {
        emit this->showTwoImages(indexPm, nextPm);
        return;
    }
    // current image is wide or the first image in archive
    emit this->showImage(futureIpmSingle.result());
}

QPixmap YasaiWorker::loadAndFitPixmap(QByteArray *qba, const QRect &viewRect, Utils::ViewOptions viewOptions, int adjustment)
{
    QPixmap pm;
    if (!pm.loadFromData(*qba)) return QPixmap();
    return this->fitPixmap(pm, viewRect, viewOptions, adjustment);
}

QPixmap YasaiWorker::fitPixmap(const QPixmap &pixmap, const QRect &viewRect, Utils::ViewOptions viewOptions, int adjustment)
{
    int scaledWidth = viewRect.width();
    int scaledHeight = viewRect.height();
    if (viewOptions.testFlag(Utils::ViewSideBySide)) scaledWidth /= 2;

    if (viewOptions.testFlag(Utils::ViewFitDoNotEnlarge) &&
            (pixmap.width()  < (scaledWidth) &&
             pixmap.height() < (scaledHeight)))
    {
        return pixmap;
    }
    if (viewOptions.testFlag(Utils::ViewFitWindow))
    {
        qreal scaleFactorHorz = static_cast<qreal>(pixmap.width()) / (scaledWidth);
        qreal scaleFactorVert = static_cast<qreal>(pixmap.height()) / (scaledHeight);
        if (scaleFactorHorz>scaleFactorVert) return pixmap.scaledToWidth(scaledWidth+adjustment, Qt::SmoothTransformation);
        return pixmap.scaledToHeight(scaledHeight+adjustment, Qt::SmoothTransformation);
    }
    if (viewOptions.testFlag(Utils::ViewFit67Visible))
    {
        qreal dscaledHeight = scaledHeight * 1.33;
        qreal dscaledWidth  = scaledWidth * 1.33;
        scaledHeight = static_cast<int>(dscaledHeight) + adjustment;
        scaledWidth  = static_cast<int>(dscaledWidth) + adjustment;
        if (pixmap.width()>pixmap.height()) return pixmap.scaledToWidth(scaledWidth, Qt::SmoothTransformation);
        return pixmap.scaledToHeight(scaledHeight, Qt::SmoothTransformation);
    }
    if (viewOptions.testFlag(Utils::ViewFitHeight))
    {
        if (viewOptions.testFlag(Utils::ViewFitDoNotEnlarge) && pixmap.height()<(scaledHeight)) return pixmap;
        qreal resizedWidth = static_cast<qreal>(scaledHeight) / pixmap.height() * pixmap.width();
        if (resizedWidth>scaledWidth) scaledHeight -= mScrollbarWidth;
        return pixmap.scaledToHeight(scaledHeight+adjustment, Qt::SmoothTransformation);
    }
    if (viewOptions.testFlag(Utils::ViewFitWidth))
    {
        if (viewOptions.testFlag(Utils::ViewFitDoNotEnlarge) && pixmap.width()<(scaledWidth)) return pixmap;
        qreal resizedHeight = static_cast<qreal>(scaledWidth) / pixmap.width() * pixmap.height();
        int scrollbarWidth = mScrollbarWidth;
        if (viewOptions.testFlag(Utils::ViewSideBySide)) scrollbarWidth /= 2;
        if (resizedHeight>scaledHeight) scaledWidth -= scrollbarWidth;
        return pixmap.scaledToWidth(scaledWidth+adjustment, Qt::SmoothTransformation);
    }

    QSize size(pixmap.size().height()+adjustment, pixmap.size().width()+adjustment);
    return pixmap.scaled(size, Qt::KeepAspectRatio, Qt::SmoothTransformation);
}
